<?php

/**
 * sfFormExt.
 *
 * @package    form
 * @version    SVN: $Id: sfDoctrineFormBaseTemplate.php 6174 2007-11-27 06:22:40Z fabien $
 */
class sfFormExt extends sfForm
{
  protected
    $labels = array(),
    $types  = array();
    
  private
    $hidden_widgets = array(),
    $normal_widgets = array(),
    $embeded_forms  = array();
  
  public function __construct($defaults = array(), $options = array(), $CSRFSecret = null)
  {
    $this->setDefaults($defaults);
    $this->options = $options;
    
    $this->setOption('required_labels', true);
    $this->initialize();

    $this->validatorSchema = new sfValidatorSchema();
    $this->widgetSchema    = new sfWidgetFormSchemaExt();
    $this->errorSchema     = new sfValidatorErrorSchema($this->validatorSchema);
    $this->setup();
    $this->configure();
    $this->fixSchemas();
    $this->addCSRFProtection($CSRFSecret);
    $this->resetFormFields();
    
    $this->postConfigure();
    
  }
  protected function initialize()
  {
    
  }
  public function postConfigure()
  {
    
  }
  public function setWidgets(array $widgets)
  {
    $this->setWidgetSchema(new sfWidgetFormSchemaExt($widgets));
  }
  public function getLabels()
  {
    return $this->labels;
  }
  public function setLabels(array $labels)
  {
    $this->labels = $labels;
  }
  public function addLabels(array $labels)
  {
    $this->labels = array_merge($this->labels, $labels);
  }
  public function getLabel($name)
  {
    return $this->labels[$name];
  }
  
  public function getTypes()
  {
    return $this->types;
  }
  public function setTypes(array $types)
  {
    $this->types = $types;
  }
  public function addTypes(array $types)
  {
    $this->types = array_merge($this->types, $types);
  }
  
  public function fixLabels()
  {
    $labels = array();
    $validatorSchema = $this->getValidatorSchema();
    foreach ($this->labels as $field => $label)
    {
      $label = rtrim($label, '*');
      if (isset($validatorSchema[$field]))
      {
        $labels[$field] = $validatorSchema[$field]->getOption('required') ? $label.'*' : $label;
      }
      else
      {
        $labels[$field] = $label;
      }
    }
    
    $this->labels = $labels;
  }
  
  public function fixSchemas()
  {
    $this->buildValidators();
    $this->unsetValidatorSchemas();
    $this->updateValidatorSchemas();
    $this->buildLabels();
  }
  
  public function buildValidators()
  {
    $validatorBuilder = new kcValidatorBuilder($this->types, $this->validatorSchema, $this->labels);
    $this->setValidatorSchema($validatorBuilder->build());
  }
  public function buildLabels()
  {
    $this->widgetSchema->setNameFormat(sprintf('%s[%%s]', $this->getName()));
    
    if ($this->getOption('required_labels'))
    {
      $this->fixLabels();
    }
    
    $this->widgetSchema->setLabels($this->labels);
  }
  
  public function unsetValidatorSchemas()
  {
  }
  
  public function updateValidatorSchemas()
  {
  }
  
  
  public function getCamelCaseName()
  {
    $class_name = get_class($this);
    $class_name = substr($class_name, 0, strpos($class_name, 'Form'));
    
    return $class_name;
  }
  public function getName()
  {
    return sfInflector::underscore($this->getCamelCaseName());
  }
  
  public function generateLazyName($name)
  {
    return $this->getWidgetSchema()->generateLazyName($name);
  }
  public function generateLazyId($name)
  {
    return $this->getWidgetSchema()->generateLazyId($name);
  }
  
  public function offsetUnset($offset)
  {
    unset
    (
      $this->labels[$offset],
      $this->types[$offset]
    );
    
    parent::offsetUnset($offset);
  }
  
  public function addWidgets(array $widgets)
  {
    foreach ($widgets as $name => $widget)
    {
      $this->widgetSchema[$name] = $widget;
    }
    
    $this->resetFormFields();
  }
  
  public function addValidators(array $validators)
  {
    foreach ($validators as $name => $validator)
    {
      $this->validatorSchema[$name] = $validator;
    }
    
    $this->resetFormFields();
  }
  
  public function removeWidgets(array $names)
  {
    foreach ($names as $name)
    {
      unset($this->widgetSchema[$name]);
      $this->types[$name] = '-';
    }
  }
  
  public function addDefaults(array $defaults)
  {
    $this->defaults = array_merge($this->defaults, $defaults);
  }
  
  protected function getCurrentRequestField($field, $form_name = null)
  {
    $form_name = $form_name ? $form_name : $this->getFormUnderscoreName();
    $params = $this->getRequest()->getParameter($form_name);
    return $params[$field];
  }
  
  protected function loadHelpers($helpers)
  {
    $configuration = sfProjectConfiguration::getActive();
  	$configuration->loadHelpers($helpers);
  }
  
  protected function getContext()
  {
    return sfContext::getInstance();
  }
  protected function getRequest()
  {
  	return $this->getContext()->getRequest();
  }
  protected function getRouting()
  {
    return $this->getContext()->getRouting();
  }
  protected function getUser()
  {
    return $this->getContext()->getUser();
  }
  protected function genUrl($uri)
  {
    return $this->getContext()->getController()->genUrl($uri);
  }
  protected function getCodeNamePostValidator()
  {
    if ($this->getObject()->isNew())
    {
      $postValidators = new sfValidatorAnd
                        (
                          array
                          (
                            new sfValidatorDoctrineUnique(array('model' => $this->getModelName(), 'column' => array('code'))),
                            new sfValidatorDoctrineUnique(array('model' => $this->getModelName(), 'column' => array('name'))),
                          )
                        );
    }
    else
    {
      $postValidators = new sfValidatorDoctrineUnique(array('model' => $this->getModelName(), 'column' => array('name')));
    }
    
    return $postValidators;
  }
  
  
  public function getWidgetSchemaPositions()
  {
    return $this->getWidgetSchema()->getPositions();
  }
  
  public function offsetGet($name)
  {
    if (!isset($this->formFields[$name]))
    {
      if (!$widget = $this->widgetSchema[$name])
      {
        throw new InvalidArgumentException(sprintf('Widget "%s" does not exist.', $name));
      }

      if ($this->isBound)
      {
        $value = isset($this->taintedValues[$name]) ? $this->taintedValues[$name] : null;
      }
      else if (isset($this->defaults[$name]))
      {
        $value = $this->defaults[$name];
      }
      else
      {
        $value = $widget instanceof sfWidgetFormSchema ? $widget->getDefaults() : $widget->getDefault();
      }

      $class = $widget instanceof sfWidgetFormSchema ? 'sfFormFieldSchema' : 'sfFormFieldExt';

      $this->formFields[$name] = new $class($widget, $this->getFormFieldSchema(), $name, $value, $this->errorSchema[$name]);
    }

    return $this->formFields[$name];
  }
  
  public function parseWidgets()
  {
    foreach ($this->getWidgetSchemaPositions() as $name)
    {
      if ($this[$name]->isHidden())
      {
        $this->hidden_widgets[] = $name;
      }
      elseif ($this->isEmbeddedForm($name))
      {
        $this->embeded_widgets[] = $name;
      }
      else
      {
        $this->normal_widgets[] = $name;
      }
    }
  }
  public function getHiddenWidgets()
  {
    return $this->hidden_widgets;
  }
  public function getNormalWidgets()
  {
    return $this->normal_widgets;
  }
  public function getEmbeddedWidgets()
  {
    return $this->embeded_widgets;
  }
  public function getOwnWidgets()
  {
    return array_merge($this->normal_widgets, $this->hidden_widgets);
  }
  
  public function hasEmbeddedForms()
  {
    return (Boolean) count($this->getEmbeddedForms());
  }
  public function isEmbeddedForm($name)
  {
    return array_key_exists($name, $this->getEmbeddedForms());
  }
  

  
  public function renderError()
  {
    if (is_null($this->parent))
    {
      throw new LogicException(sprintf('Unable to render the error for "%s".', $this->name));
    }
    
    $error = $this->getWidget() instanceof sfWidgetFormSchema ? $this->getWidget()->getGlobalErrors($this->error) : $this->error;

    return $this->parent->getWidget()->getFormFormatter()->formatErrorsForRow($error);
  }
  
  public function renderErrors()
  {
  	$widgets = array_merge($this->normal_widgets, $this->hidden_widgets);
    $errors = array();
    foreach ($widgets as $name)
    {
      if ($this[$name]->hasError())
      {
        $errors[] = $this[$name]->getError();
      }
    }
    
    return $this->getWidgetSchema()->getFormFormatter()->formatErrorRow($errors);
  }
  
  public function saveManyToMany($widget_list_name, $mainModel, $secModel, $fk_one, $fk_two, $con = null)
  {
  	if (!$secModel)
  	{
  	  $secModel = $this->getModelName();
  	}
  	
    if (!$this->isValid())
    {
      throw $this->getErrorSchema();
    }

    if (!isset($this->widgetSchema[$widget_list_name]))
    {
      // somebody has unset this widget
      return;
    }

    if (is_null($con))
    {
      $con = $this->getConnection();
    }
    
    $method = "get$mainModel";
    $object = $this->$method();
    
    $existing = array();
    $method   = sprintf('get%ss', $secModel);
    foreach ($object->$method() as $obj)
    {
      $method     = 'get'.sfInflector::camelize($fk_two);
      $existing[] = $obj->$method();
    }

    $values = $this->getValue($widget_list_name);
    if (!is_array($values))
    {
      $values = array();
    }
    
    $unlink = array_diff($existing, $values);
    if (count($unlink))
    {
      try
      {
        Doctrine::getTable($secModel)->createQuery('a')
        ->delete()
        ->where(sprintf('a.%s = ?' , $fk_one), $object->getId())
        ->andWhereIn(sprintf('a.%s', $fk_two), $unlink)
        ->execute();
      }
      catch (Exception $e)
      {
        sfContext::getInstance()->getController()->redirect('@error_delete_error');
      }
    }
    $link = array_diff($values, $existing);
    foreach ($link as $id)
    {
      $class = $secModel;
      $record = new $class;
      $method = 'set'.sfInflector::camelize($fk_one);
      $record->$method($object->getId());
      $method = 'set'.sfInflector::camelize($fk_two);
      $record->$method($id);
      $record->save();
    }
  }
}
